docker: support IPv6 nameservers in nginx resolver derivation - #79
Conversation
The entrypoint derives nginx's resolver from /etc/resolv.conf but wrote
IPv6 nameserver addresses unbracketed. On hosts with an IPv6 resolver
(e.g. Fly.io's fdaa::3) nginx fails at startup with
[emerg] invalid port in resolver "fdaa::3"
and the container crash-loops. Bracket any nameserver containing a colon
so nginx parses it as an IPv6 address instead of host:port.
Also drop the hardcoded ipv6=off, which prevented proxying to IPv6-only
upstreams (e.g. Fly.io .flycast hosts) by discarding AAAA answers.
|
Friendly bump on this one — happy to rebase or split it if that helps. Quick recap of what it fixes: The change is 5 lines in that script — bracket nameservers containing a colon, No rush at all if it's not a priority — mainly flagging it since it's a silent |
There was a problem hiding this comment.
Tested this on a rootless Podman host (Ubuntu) whose container resolv.conf includes IPv6 nameservers (addresses below replaced with documentation prefixes). It fixes the crash.
Before (current main): the container exits right away with
[emerg] invalid port in resolver "2001:db8:1400::1".
With this PR applied on main: the container starts and renders
resolver 192.0.2.1 192.0.2.2 192.0.2.3 [2001:db8:1400::1] [2001:db8:1410::1] valid=10s;
and /healthz returns 200.
The awk change is minimal and correct for plain IPv6 addresses. Dropping ipv6=off makes sense for IPv6-only upstreams. A small side effect: on hosts without IPv6 egress, nginx will also try AAAA addresses for dual-stack upstreams. It falls back to the next address, so this looks acceptable to me.
LGTM. It would be great to get this merged, because anyone with an IPv6 resolver currently can't start the image.
|
@offendingcommit could you please address this pull request? |
offendingcommit
left a comment
There was a problem hiding this comment.
Thanks for this, and sorry for the wait. Approving.
I checked it in nginxinc/nginx-unprivileged:alpine:
- IPv6 nameservers come out bracketed, and IPv4 entries (including ones with trailing comments) come out unchanged.
- With
ipv6=offremoved, a container on an IPv4-only Docker bridge still reaches an upstream that has both IPv4 and IPv6 addresses (8 of 8 requests returned 200 over IPv4, no connect errors).
One optional follow-up, not blocking. It isn't a regression, since it failed before this change too: a link-local nameserver with an interface suffix (nameserver fe80::1%eth0, common with --network host on hosts that get DNS from router advertisements) gets rendered as [fe80::1%eth0]. nginx rejects that with [emerg] invalid IPv6 address in resolver. Adding && $2 !~ /%/ to the awk pattern would skip those entries, and the 127.0.0.11 fallback still applies if nothing is left.
A small correction to the description: nginx has no happy-eyeballs fallback. It tries each resolved address in turn through proxy_next_upstream. IPv4-only hosts work because the IPv6 connect fails immediately.
Problem
docker/40-openconcho-config.shderives nginx'sresolverfrom the container's/etc/resolv.conf, but writes nameserver addresses verbatim. Two issues:IPv6 nameservers crash nginx at startup. nginx requires IPv6 resolver addresses in brackets; unbracketed, it parses everything after the last colon as a port. On any host whose resolv.conf carries an IPv6 nameserver — e.g. Fly.io machines, where the resolver is
fdaa::3— the container crash-loops with:Hardcoded
ipv6=offbreaks IPv6-only upstreams. Withipv6=off, nginx discards AAAA answers, so the header-driven/apiproxy can never reach upstreams that only resolve to IPv6 — e.g. Fly.io private.flycastaddresses.Fix
00-resolver.conf(fdaa::3→[fdaa::3]), so nginx parses it as an IPv6 address instead ofhost:port.ipv6=offso AAAA resolution works for IPv6-only upstreams. IPv4-only environments are unaffected: happy-eyeballs-style fallback still resolves A records as before.Testing
Ran the patched script inside
nginxinc/nginx-unprivileged:alpine(busybox awk, same as the image build) followed bynginx -t:/etc/resolv.confnameservers00-resolver.confnginx -tfdaa::3(Fly.io)resolver [fdaa::3] valid=10s;fdaa::3+8.8.8.8resolver [fdaa::3] 8.8.8.8 valid=10s;resolver 127.0.0.11 valid=10s;(fallback)Previously the first case rendered
resolver fdaa::3 ipv6=off valid=10s;and nginx failed with the emerg above.We hit this deploying the image on Fly.io; currently working around it with an extra entrypoint script that post-processes
00-resolver.conf, but the derivation should be fixed at the source.